package org.touch.server;

import java.io.IOException;
import java.net.*;
import java.nio.channels.*;

public class Server extends Thread{
	
	private static Server server;
	private ServerSocketChannel ssc;
	private final int SERVERPORT = 55555;
	private ClientsManager clientsManager = ClientsManager.getInstance();
	private ListenerFactory factory = ListenerFactory.getInstance();
	
	private boolean flag = true;
	
	private Server(){
		
	}
	
	public static Server getInstance(){
		if(server == null){
			server = new Server();
		}
		return server;
	}
	
	public boolean isRunning(){
		return flag;
	}
	
	public void close(){
		flag =  false;
	}

	public void run() {
		try {
			flag =  true;
			Selector selector = Selector.open();
			ssc = ServerSocketChannel.open();
			ssc.configureBlocking(false);
			ServerSocket ss = ssc.socket();
			InetSocketAddress address = new InetSocketAddress(SERVERPORT);
			ss.bind(address);
			ssc.register(selector, SelectionKey.OP_ACCEPT);
			
			while (flag) {
				selector.select();
				SocketChannel sc = null;
				for(SelectionKey key : selector.selectedKeys()){
					if (key.isAcceptable()) {
						ServerSocketChannel subssc = (ServerSocketChannel) key.channel();
						sc = subssc.accept();
						if(sc != null){
							sc.configureBlocking(false);
							sc.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
//							sc.register(selector, SelectionKey.OP_WRITE);
							clientsManager.addClientBean(sc);
						}
					} else if (key.isReadable()) {
						doRead(key);
					} else if (key.isWritable()) {
						doWrite(key);
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private void doRead(SelectionKey key) {
		SocketChannel sc = (SocketChannel) key.channel();
		ClientBean clientBean = clientsManager.getClientBean(sc);
		if(clientBean != null){
			try {
				int count = clientBean.read(sc);
				if(count > 0){
					Task task = clientBean.getTask();
					if(task != null){
						factory.execute(task);
					}
				}
			} catch (Exception e) {
				clientsManager.remove(sc);
				try {
					sc.close();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			}
		}
	}
	
	private void doWrite(SelectionKey key){
		SocketChannel sc = (SocketChannel) key.channel();
		ClientBean clientBean = clientsManager.getClientBean(sc);
		if(clientBean != null && clientBean.hasTask()){
			try {
				clientBean.write(sc);
			} catch (IOException e) {
				clientsManager.remove(sc);
				try {
					sc.close();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			}
		}
	}
}